import arrow
import datetime
import json
from flask import g, session
import bson
from flask_mongoengine.wtf.fields import DictField
from wtforms.validators import Length

from app.db.models.alert_groups import AlertGroup
from app.db.models.alertreceivers import AlertReceivers
from app.db.models.users import User
from app.errors import ParameterError, CustomNameError
from app.libs.regexp import NAME_RE, USER_EMAIL_RE, PHONE_RE
from app.libs.utility import str_id_to_object
from app.libs.enums import TaskType, WS_TASK_GROUP_TUPLE, AVAILABLE_TASK_GROUP_TUPLE
from app.validators import BaseForm, UidForm, PageForm
from wtforms import fields, Form


class AlertListVerification(PageForm):
    uid = fields.StringField()
    taskType = fields.StringField()
    taskCategory = fields.StringField()
    targetSearch = fields.StringField()
    securityStatus = fields.StringField()
    taskTriggerType = fields.StringField()
    startTime = fields.StringField()
    endTime = fields.StringField()
    username = fields.StringField()
    OR = fields.StringField()

    def validate_OR(self, value):
        if value.data:
            value.data = json.loads(value.data)
            for item in value.data:
                for k in item:
                    if k not in ('taskTriggerType', 'taskType'):
                        raise ParameterError(msg='OR format error')

    def validate_uid(self, value):
        if value.data:
            value.data = bson.ObjectId(value.data)

    def validate_page(self, value):
        value.data = int(value.data)
        if value.data < 1:
            value.data = 1

    def validate_limit(self, value):
        value.data = int(value.data)
        if value.data > 200 or value.data < 1:
            value.data = 10

    def validate_startTime(self, value):
        if value.data:
            date_str = value.data.replace('T', ' ').split('.')[0]
            value.data = datetime.datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")

    def validate_endTime(self, value):
        if value.data:
            date_str = value.data.replace('T', ' ').split('.')[0]
            value.data = datetime.datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")

    def get_query_dict(self):
        query_dict = dict()

        params = self.validate_for_api()

        query_dict['page'] = params.data['page']
        query_dict['limit'] = params.data['limit']

        for key, value in params.data.items():
            if value:
                if key == 'OR':
                    query_dict['$or'] = value
                elif key == 'targetSearch':
                    query_dict['target'] = {"$regex": value}
                elif key == 'startTime':
                    query_dict['createTime'] = {'$gte': value}
                elif key == 'endTime':
                    query_dict['createTime'] = {'$lte': value}
                elif value == "weakness":
                    query_dict[key] = {"$in": ["weakness", "vul", "ssl"]}
                else:
                    query_dict[key] = value

        if params.data['startTime'] and params.data['endTime']:
            query_dict['$and'] = [{'createTime': {'$gte': params.data['startTime']}},
                                  {'createTime': {'$lte': params.data['endTime']}}]
            query_dict.pop('createTime')

        username = query_dict.pop("username", "")
        if username:
            users = User.objects.find({"username": {"$regex": username}})
            query_dict["uid"] = {"$in": [user.pk for user in users]}

        return query_dict


class AlertGroupForm(BaseForm):
    page = fields.IntegerField(default=1, label='页码')
    count = fields.IntegerField(default=10, label='每页最大展示条数')  # 兼容
    perPage = fields.IntegerField(default=10, label='每页最大展示条数')  # 兼容

    uid = fields.StringField()
    target_url = fields.StringField()
    name = fields.StringField()
    source_ip = fields.StringField()
    alert_group_id = fields.StringField()
    switch = fields.BooleanField(default=False)
    job_ids = fields.FieldList(fields.StringField(validators=[Length(max=50)]))
    alert_group_ids = fields.FieldList(fields.StringField(validators=[Length(max=50)]))

    def validate_uid(self, value):
        self.uid = ''
        if g.role.name == '管理员':
            if not value.data:
                raise ParameterError(msg="uid param error")
            try:
                self.uid = bson.ObjectId(value.data)
            except Exception:
                raise ParameterError(msg="uid param error")

    def validate_alert_group_id(self, value):
        self.alert_group_id = ''
        if not value.data:
            return
        try:
            self.alert_group_id = bson.ObjectId(value.data)
        except Exception:
            raise ParameterError(msg="alert_group_id param error")

    def validate_alert_group_ids(self, value):
        self.alert_group_ids = []
        if not value.data:
            return
        try:
            for item in value.data:
                self.alert_group_ids.append(bson.ObjectId(item))
        except Exception:
            raise ParameterError(msg="alert_group_ids param error")

    def validate_job_ids(self, value):
        self.job_ids = []
        if not value.data:
            return
        try:
            job_ids = []
            for item in value.data:
                job_ids.append(bson.ObjectId(item))
            self.job_ids = job_ids
        except Exception:
            raise ParameterError(msg="job_ids param error")

    def check_alert_group_exist(self, uid):
        if not self.alert_group_id:
            raise ParameterError(msg='请先选择告警策略')

        alert_group = AlertGroup.objects.filter(pk=self.alert_group_id, uid=uid).first()
        if not alert_group:
            raise ParameterError(msg='当前告警策略不存在')

        return alert_group

    def get_count(self):
        if self.data.get('count') != 10:
            count = self.data.get('count')
        else:
            count = self.data.get('perPage')
        return count

    def get_notification_list(self, alert_settings):
        notification = []
        for k, v in alert_settings.get("notification", {}).items():
            if v:
                if k == 'inBox':
                    notification.append('in_box')
                else:
                    notification.append(k)
        return notification

    def get_enable_list(self, alert_settings):
        alert_settings_enable = alert_settings['enable']
        alert_type_list = (WS_TASK_GROUP_TUPLE + AVAILABLE_TASK_GROUP_TUPLE +
                           (TaskType.asset.value, TaskType.hostVul.value))

        result = []
        for k, v in alert_settings_enable.items():
            # 排除cyd告警类型
            if k not in alert_type_list:
                continue
            if v:
                result.append(k)
        return result


class AlertEnableDetail(Form):
    vul = fields.FieldList(fields.StringField())
    content = fields.FieldList(fields.StringField())
    hostVul = fields.FieldList(fields.StringField())
    securityEvent = fields.FieldList(fields.StringField())
    ssl = fields.FieldList(fields.StringField())
    changeCheck = fields.FieldList(fields.StringField())

    def validate_changeCheck(self, value):
        if not value.data:
            return
        if set(value.data) - set(["critical", "high", "medium", "low"]):
            raise ParameterError(msg="篡改监测只支持按危险等级告警")

    def validate_vul(self, value):
        if not value.data:
            return
        if set(value.data) - set(["critical", "high", "medium", "low"]):
            raise ParameterError(msg="Web漏洞监测只支持按危险等级告警")

    def validate_content(self, value):
        if not value.data:
            return
        if set(value.data) - set(["keyword", "privacy_disclosure"]):
            raise ParameterError(msg="内容违规监测只支持按类型告警")

    def validate_hostVul(self, value):
        if not value.data:
            return
        if set(value.data) - set(["critical", "high", "medium", "low"]):
            raise ParameterError(msg="主机安全监测只支持按危险等级告警")

    def validate_securityEvent(self, value):
        if not value.data:
            return
        if set(value.data) - set(["black_links", "broken_links", "cryjack", "malscan", "foreign_links"]):
            raise ParameterError(msg="安全事件监测只支持按事件类型告警")

    def validate_ssl(self, value):
        if not value.data:
            return
        if set(value.data) - set(["protocol", "certificate"]):
            raise ParameterError(msg="SSL安全监测只支持按漏洞类型告警")


class AlertGroupSettingsForm(AlertGroupForm):
    name = fields.StringField()
    receivers = fields.FieldList(fields.StringField())
    job_ids = fields.FieldList(fields.StringField())
    notification = fields.FieldList(fields.StringField())
    enable = fields.FieldList(fields.StringField())
    sms_allowed_time = DictField(default={"start": "00:00:00", "end": "23:59:59"})
    email_allowed_time = DictField(default={"start": "00:00:00", "end": "23:59:59"})
    email_daily_limit = fields.IntegerField(default=10)
    sms_daily_limit = fields.IntegerField(default=10)
    enable_detail = fields.FormField(AlertEnableDetail)

    def validate_name(self, value):
        self.name = ''
        if not NAME_RE.fullmatch(value.data):
            raise CustomNameError()
        self.name = value.data

    def validate_receivers(self, value):
        self.receivers = []
        if not value.data:
            raise ParameterError(msg="请先选择告警人")
        try:
            receivers = []
            for item in value.data:
                receivers.append(bson.ObjectId(item))
            self.receivers = receivers
        except Exception:
            raise ParameterError(msg="receivers param error")

    def validate_job_ids(self, value):
        self.job_ids = []
        if not value.data:
            raise ParameterError(msg="请先选择告警资产")
        try:
            job_ids = []
            for item in value.data:
                job_ids.append(bson.ObjectId(item))
            self.job_ids = job_ids
        except Exception:
            raise ParameterError(msg="job_ids param error")

    def validate_notification(self, value):
        self.notification = []
        notification = {"email": False, "sms": False, "wechat": False, "inBox": False}

        for item in value.data:
            if item == 'in_box':
                item = 'inBox'
            if item not in notification:
                raise ParameterError(msg="通知类型参数错误")
            notification[item] = True

        self.notification = notification

    def validate_enable(self, value):
        self.enable = dict()
        enable = {
            "asset": False,
            "vul": False,
            "cyd": False,
            "ssl": False,
            "securityEvent": False,
            "http": False,
            "ping": False,
            "hostVul": False,
            "content": False,
            TaskType.change_check.value: False,
        }

        for item in value.data:
            if item not in enable:
                raise ParameterError(msg="告警类型参数错误")
            enable[item] = True

        self.enable = enable

    def validate_sms_allowed_time(self, value):
        self.sms_allowed_time = {"start": "00:00:00", "end": "23:59:59"}
        if value.data:
            try:
                datetime.datetime.strptime(value.data["start"], '%H:%M:%S')
                datetime.datetime.strptime(value.data["end"], '%H:%M:%S')
            except Exception:
                raise ParameterError(msg="短信生效时间格式错误")
            self.sms_allowed_time = {"start": value.data["start"], "end": value.data["end"]}

    def validate_email_allowed_time(self, value):
        self.email_allowed_time = {"start": "00:00:00", "end": "23:59:59"}
        if value.data:
            try:
                datetime.datetime.strptime(value.data["start"], '%H:%M:%S')
                datetime.datetime.strptime(value.data["end"], '%H:%M:%S')
            except Exception:
                raise ParameterError(msg="邮件生效时间格式错误")
            self.email_allowed_time = {"start": value.data["start"], "end": value.data["end"]}

    def validate_email_daily_limit(self, value):
        self.email_daily_limit = 10
        if value.data >= 0:
            self.email_daily_limit = value.data

    def validate_sms_daily_limit(self, value):
        self.sms_daily_limit = 10
        if value.data >= 0:
            self.sms_daily_limit = value.data

    def check_alert_group_name_exist(self, uid):
        alert_group = AlertGroup.objects.filter(uid=uid, name=self.name).first()
        if alert_group:
            raise ParameterError(msg="策略名称已经存在")

    def check_receivers_exist(self):
        receivers = AlertReceivers.objects.filter(id__in=self.receivers)
        if not receivers:
            raise ParameterError(msg="告警人不存在")
        self.receivers = receivers.distinct("_id")

    def check_alert_detail(self):
        tmp = {
            "vul": "没有配置任何等级的Web漏洞告警",
            "securityEvent": "没有配置任何类型的安全事件告警",
            "hostVul": "没有配置任何等级的主机漏洞告警",
            "ssl": "没有配置任何类型的SSL漏洞告警",
            "content": "没有配置任何类型的内容违规告警",
            TaskType.change_check.value: "没有配置任何等级的篡改监测告警",
        }
        for t, msg in tmp.items():
            if self.enable.get(t) and not self.enable_detail.data.get(t, []):
                raise ParameterError(msg=msg)

    def validate_for_api(self):
        valid = super(AlertGroupForm, self).validate()
        if valid:
            pass
        else:
            raise ParameterError(msg=self.errors)
        self.check_alert_detail()
        return self


class AlertReceiversListForm(PageForm, UidForm):
    pass


class AddReceiverForm(UidForm):
    name = fields.StringField()
    email = fields.StringField()
    phone = fields.StringField()
    email_code = fields.StringField()
    phone_code = fields.StringField()

    def validate_name(self, value):
        if not value.data:
            raise ParameterError(msg="请输入告警人名称")
        if not NAME_RE.fullmatch(value.data):
            raise CustomNameError()

    def validate_email(self, value):
        if value.data and not USER_EMAIL_RE.fullmatch(value.data):
            raise ParameterError(msg='请输入正确的邮箱')

    def validate_phone(self, value):
        if value.data and not PHONE_RE.fullmatch(value.data):
            raise ParameterError(msg='请输入正确的手机号码')

    def validate_params(self):
        phone = self.phone.data
        email = self.email.data
        phone_code = self.phone_code.data
        email_code = self.email_code.data

        if (not phone) and (not email):
            raise ParameterError(msg='请填入至少一个联系方式')

        if email and not email_code:
            raise ParameterError(msg='请填写邮件验证码')

        if phone and not phone_code:
            raise ParameterError(msg='请填写手机验证码')


class EditReceiverForm(AddReceiverForm):
    receiver_id = fields.StringField()

    def validate_receiver_id(self, value):
        if not value.data:
            raise ParameterError(msg="id参数错误")
        try:
            bson.ObjectId(value.data)
        except Exception:
            raise ParameterError(msg="id参数格式错误")


class DeleteReceiverForm(UidForm):
    receiver_ids = fields.FieldList(fields.StringField())

    def validate_receiver_ids(self, value):
        self.receiver_ids = []
        try:
            self.receiver_ids = str_id_to_object([i.data for i in value])
        except Exception:
            raise ParameterError(msg="参数格式错误")
        if not self.receiver_ids:
            raise ParameterError(msg="没有选择任务数据")


class ReceiverVerifiedForm(UidForm):
    email = fields.StringField()
    phone = fields.StringField()

    def validate_email(self, value):
        if value.data:
            if not USER_EMAIL_RE.match(value.data):
                raise ParameterError(msg='请输入正确的邮箱')

    def validate_phone(self, value):
        if value.data:
            if not PHONE_RE.match(value.data):
                raise ParameterError(msg='请输入正确的手机号码')
